home *** CD-ROM | disk | FTP | other *** search
/ Shareware Grab Bag / Shareware Grab Bag.iso / 003 / db3n8509.arc / DB3N8509.TXT
Text File  |  1985-09-30  |  47KB  |  1,223 lines

  1. 1  dBASE III Anomalies and Workarounds
  2.  
  3. 1.1  How to use this Section
  4.  
  5.  
  6. 1.2  @..SAY...GET to @...SAY...PICTURE
  7.  
  8. >>> & Function
  9.  
  10. The  macro (&)  function will  not expand properly if  it is followed  by a
  11. space and parentheses.  For example:
  12.  
  13.  
  14.      STORE 'LIST FOR' TO x
  15.      STORE 10 TO memvar
  16.      &x Field1 = memvar
  17.  
  18.  
  19. will execute properly, but,
  20.  
  21.      &x (Field1 - memvar) * memvar = 0
  22.  
  23. will return the "*** Unrecognized command verb" error message. This problem
  24. can be avoided by terminating the macro-substituted memory  variable with a
  25. period.  For example,
  26.  
  27.      &x. (Field1 - memvar) * memvar = 0
  28.  
  29. will work.  It is always a good idea to terminate a macro with a period.
  30.  
  31.  
  32.  
  33.  
  34. 1.3  APPEND to CONFIG.DB
  35.  
  36. >>> CONFIG.DB with TEDIT or WP
  37.  
  38. Config.db will not accept more than eight characters for WP or  TEDIT.  Any
  39. more than eight will be truncated.  Attempting to use MODIFY COMMAND (or to
  40. edit a MEMO field)  will briefly display the operating  system message "Bad
  41. command or file name"  and drop the user to the dBASE dot prompt (or to the
  42. edit screen).  dBASE III warns the user that the filename is truncated when
  43. dBASE is initialized.  For example:
  44.  
  45.      TEDIT=B:DFORMAT
  46.                    ^--- truncated
  47.  
  48. To work around this problem, place the word processor in the same drive and
  49. directory or rename the wordprocessor with fewer characters.
  50.  
  51.  
  52.  
  53. 1.4  COPY [STRUCTURE] to DO WHILE
  54.  
  55. >>> DO WHILE with RESTORE
  56.  
  57. If a memory variable tested in a DO WHILE loop is recreated in  the loop by
  58. RESTOREing the variable FROM a memory file,  the loop will  continue to run
  59. even after the  condition no longer evaluates as  true  (.T.).  The program
  60. below will run endlessly as long as the control variable  is  not the first
  61. entry in the memory file:
  62.  
  63.      var = .T.
  64.      DO WHILE var
  65.         RESTORE FROM Memfile <--- This overwrites var at the
  66.                                   same memory location.
  67.         var = .F.            <--- This change is ignored if
  68.      ENDDO                        the previous assignment
  69.                                   statement changed the
  70.                                   memory location of the
  71.                                   variable.
  72.  
  73. RESTOREing ADDITIVE ameliorates the problem.
  74.  
  75.  
  76. >>> DO WHILE with semicolon
  77.  
  78. When a DO WHILE conditional statement is continued to a second line  with a
  79. semicolon,  dBASE  III tries to  execute this  second line the  second time
  80. through  the  loop.   When  the  ENDDO  is  encountered  and  the condition
  81. evaluates as true,  program flow proceeds to this second line, resulting in
  82. the error message, "*** Unrecognized command verb." For example:
  83.  
  84.           * ---This program will give an error message
  85.           * ---when "Y" is entered at the WAIT prompt.
  86.           answer = 'Y'
  87.           number = 1
  88.           DO WHILE number < 10;
  89.              .AND. answer = 'Y'
  90.              ? number
  91.              WAIT '"Y" to continue ' TO answer
  92.              number = number + 1
  93.           ENDDO
  94.  
  95. If "Y"  is entered at  the WAIT prompt,  dBASE III tries to  execute ".AND.
  96. answer = 'Y'." However, if the semicolon is deleted and the line is allowed
  97. to wrap at column 67 in MODIFY COMMAND, execution flows correctly.
  98.  
  99.  
  100. >>> DO WHILE with RETURN
  101.  
  102. A RETURN  statement inside a  DO WHILE...ENDDO construction will  not close
  103. the DO WHILE <condition> in the program as it does in dBASE II.  Therefore,
  104. the <condition> will continue to be evaluated.  For example:
  105.  
  106.      * A.PRG
  107.      expA = "1"
  108.      DO WHILE expA = "1"
  109.         ? "Hi!"
  110.         DO B     --------------->  * B.PRG
  111.      ENDDO                         expB = "2"
  112.      * EOF: A.PRG                  DO WHILE expB = "2"
  113.                                       ? "How are you?"
  114.                                       expA = "X"
  115.                                       RETURN
  116.                                    ENDDO
  117.                                    * EOF: B.PRG
  118.  
  119. Executing  A.PRG  will  cause  an  infinite  loop.   It  appears  dBASE III
  120. continues to  test the  condition of expB.   In order to  work around this,
  121. B.PRG should be written as follows:
  122.  
  123.           * B.PRG (revised).
  124.           expB = "2"
  125.           DO WHILE expB = "2"
  126.              ? "How are you?"
  127.              expA = "X"
  128.              EXIT             <----- Notice, the EXIT here,
  129.           ENDDO
  130.           RETURN              <----- and the RETURN here.
  131.           * EOF: B.PRG
  132.  
  133.  
  134. >>> DO WHILE with an extra ENDDO
  135.  
  136. If an extra ENDDO is added to a command file, an infinite loop will result.
  137. For example, the following program will execute until Esc is pressed:
  138.  
  139.         number = 0
  140.         DO WHILE number < 10
  141.            @ 10,10 SAY "Now at loop " + STR(number,2)
  142.            STORE number + 1 TO number
  143.         ENDDO
  144.         ENDDO           <--- This ENDDO has no matching DO WHILE.
  145.         RETURN
  146.  
  147. We recommend you use some method of indentation for the control structures:
  148. DO CASE...ENDCASE,  DO WHILE...ENDDO, and IF...ENDIF to avoid this problem.
  149. This  practice will make  command files more  readable,  and will allow for
  150. quick visual  checking for accuracy of  nested control  structures.  In our
  151. example,  two consecutive ENDDO statements with the same  left  margin is a
  152. definite indication that something is wrong.
  153.  
  154.  
  155.  
  156. 1.5  EJECT to LABEL limitations
  157.  
  158.  
  159. 1.6  MODIFY STRUCTURE to PCOL() and SET MARGIN TO
  160.  
  161. >>> Memo fields, listing or printing after a previous field
  162.  
  163. A problem displaying memo fields has been found in dBASE III by one  of our
  164. users.   The  problem occurs  when  you try to  list or print a  memo field
  165. preceded  by  a field whose length  will force  the first line  of the memo
  166. field to wrap around one or more times.
  167.  
  168. Assume a file structure consisting of two fields:
  169.  
  170.  
  171.      Structure for database: Example.DBF
  172.      Field  Field Name  Type       Width    Dec
  173.          1  Field1      Character    100
  174.          2  Field2      Character     70
  175.          3  Comments    Memo          10
  176.      ** Total **                     181
  177.  
  178. These display commands
  179.  
  180.      ? SPACE( 1 ), Comments
  181.      ? Field1, Comments
  182.      ? Field2, Comments
  183.      ? Field1, Field2, Comments
  184.  
  185. will all produce different results.
  186.  
  187. If the total length of the fields preceding the memo field is 29  or above,
  188. the first line of the memo field will wrap  around.   The entire memo field
  189. will then be displayed in double space.  Futhermore, if the total length of
  190. the fields preceding the memo field is long enough to force the  first line
  191. of  the memo  field to  be  displayed starting  on  the second  line of the
  192. display, then the entire memo field will be displayed in triple space.
  193.  
  194.  
  195.  
  196.  
  197. 1.7  RECNO() to ROUND()
  198.  
  199. >>> REPORT FORM with right margin=report width
  200.  
  201. CREATEing a REPORT FORM with a right margin value equal to the report width
  202. (the page width minus the left margin)  will display garbage to  the screen
  203. or printer.  This happens beacuse there is no space to print the report.
  204.  
  205. There is a general misconception about the meaning of  the right  margin in
  206. the  REPORT  FORM.   Some users have the impression  that its  value is the
  207. number of characters from the left margin,  much the same  way a typewriter
  208. works.   The value actually refers the the number  of  characters the right
  209. margin is offset from page width.
  210.  
  211. For example,
  212.  
  213.      page width    |------------------------------------->|
  214.      right margin                             |<----------|
  215.  
  216.  
  217. >>> REPORT FORM with stacked columns
  218.  
  219. CREATEing a REPORT FORM field that  consists of stacked columns  built with
  220. the result of the STR()  function and numeric fields  may cause  numbers to
  221. display incorrectly.  Specifically, entering:
  222.  
  223.      STR( Field1, 5 ) + STR( Field2, 5 )
  224.  
  225. in the field contents and specifying a column width of five  will result in
  226. a correct display  only when the fields  have five-digit numbers  stored in
  227. them.   If the number  has less  than  five  digits,  the  display  will be
  228. misplaced by the number of digits missing.  The example above will produce:
  229.  
  230.         11111
  231.         11111
  232.  
  233. if the fields are full, but:
  234.  
  235.          1111
  236.         1111
  237.         1111
  238.  
  239. if there are only four digits in the fields.
  240.  
  241.  
  242.  
  243.  
  244. 1.8  SET <full-screen> to SORT
  245.  
  246. >>>SET FILTER TO with GO BOTTOM
  247.  
  248. If  a SET FILTER  TO  condition is  not satisfied  by  any  records  in the
  249. database  file  and a GO  BOTTOM is issued,  both the EOF()  and BOF() will
  250. return a true (.T.).   Removing the filter by issuing a SET FILTER  TO does
  251. not reset EOF() or BOF().  The record pointer must be repositioned to reset
  252. EOF()  and  BOF().   SKIP or SKIP -1,  however,  will return a file boundry
  253. error message,  because EOF()  and  BOF()  are  true.   To  move the record
  254. pointer appropriately issue a GO TOP and the BOF() and EOF() values will be
  255. reset to false (.F.).  
  256.  
  257.  
  258.  
  259. 1.9  STORE to ZAP
  260.  
  261.  
  262. 2  dBASE III Programming
  263.  
  264.  
  265. 3  dBASE III Frequently Asked Questions
  266.  
  267. 3.1  Installation
  268.  
  269.  
  270. 3.2  Commands
  271.  
  272.  
  273. 3.3  New data types
  274.  
  275.  
  276. 3.4  Memory variables
  277.  
  278.  
  279. 3.5  Printing
  280.  
  281.  
  282. 3.6  Data transfer
  283.  
  284.  
  285. 4  dBASE III Reference
  286.  
  287. 4.1   @...GET to Boolean Operators
  288.  
  289.  
  290. 4.2   CHR() to FILE() function
  291.  
  292.  
  293. 4.3   FIND to MODIFY STRUCTURE
  294.  
  295. >>> Get Current Directory
  296.  
  297. dBASE III has no facility to get the name of the current directory.  To get
  298. it you must RUN the PC/MS-DOS command CD and import the results  into dBASE
  299. III.  The basic algorithm is as follows:
  300.  
  301.      1.   Create or have available a general-purpose  database  file called
  302.         Util.DBF.   Util.DBF  has  one  field  called  Util_line  which  is
  303.         character type and has a length of 80.
  304.  
  305.      2.   RUN the PC/MS-DOS command CD,  piping the result into a text file
  306.         entitled Util.TXT.
  307.  
  308.      3. APPEND the text file Util.TXT into the database file, Util.DBF.
  309.  
  310.      4.  Assign to a memory variable the name of the current  DOS directory
  311.         from Util.DBF.
  312.  
  313. The code that will execute this algorithm is as follows:
  314.  
  315.      SET SAFETY OFF
  316.      RUN CD > Util.TXT
  317.      USE Util
  318.      ZAP
  319.      APPEND FROM Util SDF
  320.      currdir = TRIM( Util_line )
  321.      SET SAFETY ON
  322.      RETURN
  323.  
  324.  
  325. >>> Get Diskspace
  326.  
  327. In the Developer's Release use the  DISKSPACE()  function to get the amount
  328. of space left on the currently  logged drive.   The DISKSPACE() will return
  329. the number of free bytes on the default drive as a numeric value.
  330.  
  331. dBASE III versions 1.0  and 1.1 do not have a function to return the amount
  332. of space left on the default drive.   So, to get the amount of diskspace in
  333. these  versions,  use the PC/MS-DOS  utility CHKDSK and  import the results
  334. into dBASE III.  The basic algorithm is as follows:
  335.  
  336.      1.   Create or have available a general purpose  database  file called
  337.         Util.DBF.   Util.DBF  has  one  field  called  Util_line  which  is
  338.         character type and has a length of 80.   This database file will be
  339.         useful for any of these kinds of survey operations into PC/MS-DOS.
  340.  
  341.      2.   RUN CHKDSK  including the designator  of the drive  for which you
  342.         want the space statistic for,  and pipe the result into a text file
  343.         entitled  Util.TXT.   Piping is a PC/MS-DOS capability  that allows
  344.         the results of a program to be sent into  a text file.   It is very
  345.         useful  for passing parameters  between programs  when  there is no
  346.         formalized interface.  The syntax is:
  347.  
  348.                  <DOS commmand>  >  <result text file>
  349.  
  350.                                 ^_____ DOS piping symbol
  351.  
  352.         For  more  information on  this capability,  consult your PC/MS-DOS
  353.         reference manual.
  354.  
  355.      3. APPEND the text file, Util.TXT, into the database file, Util.DBF.
  356.  
  357.      4.   Assign to  a memory  variable  the number  of  free  bytes on the
  358.         specified  drive from  Util.DBF.   This operation requires that you
  359.         GOTO the record that contains  the free disk space  information and
  360.         then extract the number of bytes from the field  using the SUBSTR()
  361.         function.
  362.  
  363.         The following is a  LIST of Util.DBF  with the results  of a CHKDSK
  364.         report.   When  APPENDed into a database  file,  the first and last
  365.         records are always blank.   Records 2  through 6 contain statistics
  366.         about the currently logged  disk  drive.   Note  that  this  is the
  367.         currently logged DOS drive and not  the DEFAULT drive SET  in dBASE
  368.         III.    Records  8  and  9  contain  statistics  about  the  memory
  369.         configuration of  your  computer.   The number  of  bytes  for each
  370.         attribute of the drive and memory occupy  positions 1  through 9 in
  371.         the database field, Util_line.
  372.  
  373.         Record#
  374.               1
  375.               2    9965568 bytes total disk space
  376.               3     155648 bytes in 4 hidden files
  377.               4      90112 bytes in 22 directories
  378.               5    6000640 bytes in 397 user files
  379.               6    3719168 bytes available on disk
  380.               7
  381.               8     524288 bytes total memory
  382.               9     122480 bytes free
  383.              10
  384.  
  385. The code that will get the the number of free  bytes on  the specified disk
  386. drive is as follows:
  387.  
  388.      SET SAFETY OFF
  389.      RUN CHKDSK > Util.TXT
  390.      USE Util
  391.      ZAP
  392.      APPEND FROM Util SDF
  393.      GO 6
  394.      diskspace = STR( SUBSTR( Util_line, 1, 9 ), 9 )
  395.      USE
  396.      SET SAFETY ON
  397.      RETURN
  398.  
  399.  
  400. >>> Get Last Update and Time
  401.  
  402. To get the date of last update for the currently SELECTed database file in
  403. the Developer's Release of dBASE III, use the LUPDATE() function.
  404. LUPDATE() returns the date of last update as a value of date type.
  405.  
  406. dBASE III versions 1.0 and 1.1 currently do not have a function that
  407. returns the date of last update for the SELECTed database file.  To get the
  408. date of last update in these versions of dBASE III, use the PC/MS-DOS
  409. command DIR, and import the results into dBASE III.  The basic algorithm is
  410. as follows:
  411.  
  412.      1. Create or have available a general purpose database file called
  413.         Util.DBF.  Util.DBF has one field called Util_line which is
  414.         character type and has a length of 80.
  415.  
  416.      2. RUN the PC/MS-DOS command DIR with the name of your database file,
  417.         piping the result into a text file entitled Util.TXT.
  418.  
  419.      3. APPEND the text file Util.TXT into the database file Util.DBF.
  420.  
  421.      4. Assign to a memory variable the date of last update from Util.DBF
  422.         for your database file.
  423.  
  424. The code that will get the last update of the currently SELECTed database
  425. file is as follows:
  426.  
  427.  
  428.      SET SAFETY OFF
  429.      RUN DIR <Yourfile>.DBF > Util.TXT
  430.      USE Util
  431.      ZAP
  432.      APPEND FROM Util SDF
  433.      lupdate = SUBSTR( Util_line, 25, 8 )
  434.      luptime = SUBSTR( Util_line, 34, 6 )
  435.      USE
  436.      SET SAFETY ON
  437.      RETURN
  438.  
  439.  
  440. >>>Installation
  441.  
  442. If  the message "Insert System  Disk  #2  or press Ctrl-Break" appears when
  443. dBASE III is being loaded from  a hard  disk and is installed,  the overlay
  444. file  (DBASE.OVL)  is corrupted.   One possibility is that the  copy on the
  445. hard  disk  is  corrupted and can no  longer  be  used.   Another, and more
  446. likely,  possibility is that the copy on System Disk #2 is bad, and you are
  447. now trying to run dBASE III after just  having installed to the  hard disk.
  448. If this occurs, contact the Ashton-Tate Customer Service Department.
  449.  
  450.  
  451. >>> MEMO fields
  452.  
  453. (1)  MEMO  fields  are used  to  contain up  to  5,000  characters  of text
  454. information that is to be  associated with a database  record.  Information
  455. may  be  read into a MEMO  field using Ctrl-K-R  and written to  text files
  456. using Ctrl-K-W.   Information from MEMO fields can be  displayed or printed
  457. by using  LIST,  DISPLAY,  ?.   The  field  must  be  specified  with these
  458. commands.   However,  these  commands  cause the MEMO  field to  wrap at 50
  459. columns.   The REPORT  FORM  may be  used  to output MEMO  fields with line
  460. widths of more or less than 50 characters.
  461.  
  462. (2)  PACKing a database file with memo fields will not decrease  the amount
  463. of disk space used by the .DBT file.   The command file  below demonstrates
  464. how to remove the deleted records and free the unused disk space.
  465.  
  466.           SET DELETED ON
  467.           USE Filea
  468.           COPY TO Temp
  469.           CLOSE DATABASE
  470.           ERASE Filea.dbf
  471.           ERASE Filea.dbt
  472.           RENAME Temp.dbf TO Filea.dbf
  473.           RENAME Temp.dbt TO Filea.dbt
  474.           SET DELETED OFF
  475.  
  476.  
  477.  
  478.  
  479. 4.4   Numeric fields to PARAMETERS
  480.  
  481.  
  482. 4.5   PRIVATE to PROCEDURE
  483.  
  484.  
  485. 4.6   PUBLIC to REPORT FORM
  486.  
  487. >>> Ramdisk
  488.  
  489. There  are  several  options  for  users  who  wish  to  use  a  ramdisk in
  490. combination with dBASE III.
  491.  
  492.      1.  For faster operation of applications that  utilize routines stored
  493.         in the DBASE.OVL file, you may wish to put the .OVL in a ramdisk.
  494.  
  495.         (a)  The minimum drive size  will have to  be in excess  of 181,000
  496.             bytes.   The DBASE.OVL file  for version 1.1  is 180,736 bytes.
  497.             The  total amount  of  RAM in  your  machine must  be more than
  498.             440,000  bytes in order to do  this.   Additionally, if you are
  499.             using a CONFIG.DB,  it must be present on the  drive where .OVL
  500.             resides.
  501.  
  502.         (b)  Boot dBASE III from the ramdisk by  calling for  the DBASE.COM
  503.             from the drive on which it resides.   For example:  drive D: is
  504.             the ramdisk and the DBASE.COM is on the C: drive.
  505.  
  506.             C> D:
  507.             D> C:DBASE
  508.  
  509.  
  510.  
  511.      2.   It may be a very useful area for procedure or command files to be
  512.         run from,  increasing the speed of  processing.   Prior to entering
  513.         dBASE III,  copy  the appropriate files to  the  ramdisk.   Once in
  514.         dBASE III, SET the DEFAULT TO the ramdisk drive and proceed.
  515.  
  516.      3.   It is also useful as a small work area to  manipulate utility and
  517.         temporary files.   The useage tips on getting the current directory
  518.         or diskspace are good  examples of where a small  ramdisk  would be
  519.         extremely useful and time efficient.
  520.  
  521.  
  522. >>> REPORT FORM
  523.  
  524. The  semicolon   is  not   documented   as   functioning   as   a  Carriage
  525. Return/Line-Feed in certain parts of REPORT FORMs.
  526.  
  527.  
  528. >>> REPORT FORM grouped by week
  529.  
  530. If you have a date-oriented report and you need to have it grouped by week,
  531. the following discussion will assist you.
  532.  
  533. The grouping of dates into weeks has two requirements.  First, the database
  534. file you are reporting from must be INDEXed on the date field that is being
  535. grouped on.   Second, as the group expression in your REPORT FORM, you must
  536. have an expression that returns as its value the first day of the  week for
  537. each date field.  The expression is as follows:
  538.  
  539.      Yourdate - ( DOW( Yourdate ) - 1 )
  540.  
  541. When given any date value, this expression returns the date of the previous
  542. Sunday.   It does this  by subtracting from your  date field  the number of
  543. days that have passed since the last Sunday,  the first day of  the week in
  544. the dBASE III calendar.   This value is obtained by subtracting 1  from the
  545. result of the DOW() function.
  546.  
  547. If you wish to have the week you are grouping on start on a  later day such
  548. as Monday,  subtract more  from  the result  of  the  DOW()  function.  For
  549. example, Monday would be DOW() - 2, Tuesday DOW() - 3, and so on.
  550.  
  551.  
  552.  
  553.  
  554. 4.7   RELEASE to SET PROCEDURE
  555.  
  556.  
  557. 4.8   SET RELATION to warnings
  558.  
  559. >>>WAIT TO
  560.  
  561. When a function key is SET to a literal character string,  the WAIT command
  562. will not accept the assigned string, although ACCEPT TO will.  Instead, the
  563. WAIT command will take the ASCII code  respresentation of  the function key
  564. itself.  For example:
  565.  
  566.    SET FUNCTION 10 TO "A"
  567.    WAIT TO var
  568.  
  569.    * ---Press F10.
  570.    DISPLAY MEMORY
  571.  
  572.    VAR        pub   C  "v"
  573.        1 variables defined,        3 bytes used
  574.      255 variables available,   5997 bytes available
  575.  
  576.    * ---Test to see what ASCII character F10 sent.
  577.    ? ASC(var)
  578.    246 <------------------------- Code for F10
  579.    ? ASC("A")
  580.    65 <-------------------------- Code for "A"
  581.  
  582.  
  583.  
  584.  
  585. 4.9   dBase III File Structure
  586.  
  587.  
  588. 4.10  dBASE III Memo File Structure
  589.  
  590.  
  591. 4.11  Installation and Configuration
  592.  
  593.  
  594. 5  dBASE III Sample Programs
  595.  
  596.  
  597. 6  dBASE III Technical Notes
  598.  
  599. 6.1  Shifted Data Displays
  600.  
  601.    by OLiver Biggerstaff
  602.  
  603. A database file may become corrupted for any number of reasons.   Often the
  604. corruption may be  the form  of  shifted data  in  full-screen  edit screen
  605. displays.   This is caused by  an embedded null  character in  a record.  A
  606. null character is represented as a 00 hex and is used by dBASE II and dBASE
  607. III as a string terminator.  A string terminator is a character that can be
  608. thought of as a delimiter,  much like double quotes surrounding a character
  609. string, or as a carriage return and linefeed at the end of a record.
  610.  
  611. The dBASE APPEND,  EDIT, BROWSE, and other full-screen commands work on the
  612. principle that the cursor's  positioning on  the screen  depends on certain
  613. attributes,  such as the length of a field and its  current  position.  The
  614. appearence of  shifted data  is  caused  by the  embedded string terminator
  615. forcing  dBASE to  stop  the display of  a field  prematurely,  placing the
  616. cursor at an incorrect location.  If a null character is encountered before
  617. all the characters of a field have been displayed,  dBASE will stop listing
  618. that particular field and will produce the shift effect  by  displaying the
  619. next field at an incorrect screen location.
  620.  
  621. The data  is actually not shifted physically  in  the database  file. It is
  622. simple,  therefore, to correct the database by replacing any null character
  623. with another character that does not force dBASE to display incorrectly.  A
  624. good choice for this character is the ASCII character zero  (0)  or 30 hex.
  625. Replacing a null with this character is advantageous for two reasons:
  626.  
  627.      1. Since dBASE can display it, you can locate the corrupted data.
  628.  
  629.      2.   A  zero  character will have the least disruptive  effect  on the
  630.         contents of the  database file.   Additionally, logical fields that
  631.         contain a zero character will be displayed as (.F.)
  632.  
  633. Once  all the null  characters  have  been  replaced,  the  BROWSE  or EDIT
  634. commands can be used to retype the original data in place of the characters
  635. that replaced the nulls. 
  636.  
  637. The  following is  one of  many  methods that  can be used to  replace null
  638. characters with  other characters.   In  this example we use  the PC/MS-DOS
  639. utility DEBUG.COM, since most of you have this program on your supplemental
  640. PC/MS-DOS  disk.   These examples assume that  the database file is  64K or
  641. less in size.   Refer to the PC/MS-DOS manual for more information on DEBUG
  642. and how to use data segments if the database file is larger than 64K.
  643.  
  644. The contents contained in the <  and >  symbols must be calculated  by you,
  645. and entered without the symbols.   For  example,  if  the value  of  the CX
  646. register is 2C80H,  then <CX+100H>  is to be replaced with  2D80H.  Be sure
  647. that  before  you attempt this  procedure,  you have made a backup  of your
  648. database file.
  649.  
  650. For dBASE II users on a 16-bit computer:
  651.  
  652.      C>DEBUG <database>.DBF ;Read database file into memory.
  653.      -RCX                   ;Get the value in the CX
  654.                             ;register.
  655.      -S 309 <CX+100H> 00    ;Search for nulls in the file.
  656.        .
  657.        .                    ;A list appears here of one or
  658.        .                    ;more addresses containing a
  659.        .                    ;null.
  660.      -E <address> 30        ;Replace each individual address
  661.                             ;that contains a null with a
  662.                             ;zero.
  663.      -W                     ;Save the modifications to disk.
  664.      -Q                     ;Quit DEBUG.
  665.  
  666. For dBASE III users:
  667.  
  668.      C>DEBUG <database>.DBF ;Read database file into memory.
  669.      -RCX                   ;Get the value of the CX
  670.                             ;register.
  671.      -S 100H 1121H 0DH      ;Search for the end of the
  672.                             ;header for address containing
  673.                             ;0DH
  674.      xxxx:yyyy              ;for address containing 0D.
  675.                             ;Search for null characters.
  676.  
  677.      -S <yyyy+2> <CX+100H> 00
  678.  
  679.        .
  680.        .                     ;List of addresses containing
  681.        .                     ;nulls.
  682.        .
  683.      -E <address> 30H        ;Replace each null with a zero.
  684.      -W                      ;Save the modifications to
  685.                              ;disk.
  686.      -Q                      ;Quit DEBUG.
  687.  
  688. Unfortunately, this method of replacing null characters can be very tedious
  689. if many characters must be replaced.   For those of you with a large amount
  690. of corrupted data,  it is suggested  that you use  other well-known utility
  691. programs such as NIBBLER,  NORTON UTILITIES, JAZ, or PATCH.  These programs
  692. will allow you to look at very large database files directly from  the hard
  693. disk.   Some of these  programs may also have  a global  search and replace
  694. option.
  695.  
  696.  
  697. 6.2  Swapping Printer Ports on the IBM PC
  698.  
  699.      by Robert Boies
  700.  
  701. Last month in the dBASE II section of TechNotes we presented an assembler
  702. routine to swap printer ports on an IBM PC.  This routine allows the user
  703. or programmer to toggle the LPT1 and LPT2 ports, making it easy to redirect
  704. output to several printers.  This month we present essentially the same
  705. routine configured for dBASE III.
  706.  
  707. If you have versions 1.0 or 1.1 of dBASE III, you will have to create an
  708. .COM file from this routine and at runtime use the RUN command to call it.
  709. For example from within dBASE III,
  710.  
  711.       * ---Print a first report to the default port, LPT1.
  712.       REPORT FORM One TO PRINT
  713.  
  714.       * ---Print a second report to LPT2.
  715.       RUN Portswap <---------------------------- Redirect to LPT2.
  716.       REPORT FORM Two TO PRINT
  717.       RUN Portswap <---------------------------- Restore LPT1.
  718.  
  719.  
  720. If you have the Developer's Release of dBASE III, you will be able to use
  721. the assembly language interface implemented in that version.  For example:
  722.  
  723.       * ---LOAD into memory.
  724.       LOAD Portswap
  725.  
  726.       * ---Print a first report to to the default port, LPT1.
  727.       REPORT FORM One TO PRINT
  728.  
  729.       * ---Print a second report to LPT2.
  730.       CALL Portswap <---------------------------- Redirect to LPT2.
  731.       REPORT FORM Two TO PRINT
  732.       CALL Portswap <---------------------------- Restore LPT1.
  733.  
  734.       * ---RELEASE the memory space.
  735.       RELEASE MODULE Portswap
  736.  
  737. The listing below, Portswap.ASM, uses the conditional directives discussed
  738. earlier to allow you to specify whether you wish to create a .BIN or .COM
  739. file.
  740.  
  741. Assembly language source code:
  742.  
  743. ; Program ..: Portswap.ASM
  744. ; Author ...: Robert Boies
  745. ; Date .....: September 1, 1985
  746. ; Note .....: Swaps the LPT1 and LPT2 ports in PC/MS-DOS 2.x
  747.  
  748.         .LFCOND               ; List false conditionals.
  749.         PAGE 60,132           ; Page length 60, line 132.
  750.  
  751. COM     EQU      0            ; Assemble as .BIN file
  752. D3      EQU      1            ; for Developer's Release.
  753. ;
  754. CODESEG SEGMENT BYTE PUBLIC 'CODE'
  755.         ASSUME    CS:CODESEG
  756. ;
  757. PORTSWAP PROC   FAR
  758. ;
  759.         IF COM
  760.           ORG    0100H       ; Originate at 0100H.
  761.         ENDIF
  762. ;
  763. START:  PUSH     ES          ; Save environment.
  764.         PUSH     AX
  765.         PUSH     BX
  766.         PUSH     DI
  767. ;
  768.          MOV     AX,40H      ; System stores critical operating
  769.                              ; parameters at segment 40H (absolute
  770.                              ; address 400H).
  771.                              ; Load this segment address into AX.
  772.         PUSH     AX          ; Put it on the stack.
  773.         POP      ES          ; Pop the segment id from the
  774.                              ; stack into extra segment register.
  775.         MOV      DI,8H       ; Port address of LPT1 and LPT2 are
  776.                              ; stored at offsets 8H and 0AH in
  777.                              ; this segment.  Load DI with offset.
  778.         MOV      AX,ES:[DI]  ; Put the port address of the
  779.                              ; first printer into the accumulator.
  780.         INC      DI          ; Increment the destination index
  781.                              ; twice to point to port address
  782.         INC     DI           ; of the second printer.
  783.         MOV     BX,ES:[DI]   ; Put the port address of the
  784.                              ; second printer into BX.
  785.         MOV    ES:[DI],AX    ; Poke the address of the first
  786.                              ; printer into the location of the
  787.                              ; second printer.
  788.         DEC    DI            ; Decrement DI twice to point to the
  789.         DEC    DI            ; location of the first printer
  790.                              ; port address.
  791.         MOV    ES:[DI],BX    ; Poke the address of the second
  792.                              ; printer into the location of the
  793.                              ; first.
  794.         POP    DI            ; Restore environment.
  795.         POP    BX
  796.         POP    AX
  797.         POP    ES
  798.  
  799.         IF     COM
  800.           INT  20H           ; INT 20H if .COM file
  801.         ELSE
  802.           RET                ; far return to dBASE III
  803.         ENDIF
  804. ;
  805. PORTSWAP ENDP
  806. CODESEG  ENDS
  807.          END      START
  808.  
  809.  
  810.  
  811. 6.3  Interfacing Assembly Language Routines with dBASE
  812.  
  813.    by Ralph Davis
  814.  
  815. Creating Assembler Programs with DEBUG
  816.  
  817. DEBUG is  the assembly  language  programmer's  best  friend.   It is a powerful  tool for
  818. exploring  the computer's  memory,  testing  assembly language programs,  studying program
  819. listings,  and creating new programs.   Additionally,  it can be used to rebuild corrupted
  820. data files,  convert hidden files to accessible files,  or simply analyze file structures.
  821. Our main interest in DEBUG here is to create assembly language routines for use with dBASE
  822. II and dBASE III.
  823.  
  824. It is tempting to use DEBUG because of  its interpreter-like  qualities.   You can quickly
  825. enter code and then see if it works.If it does,  you call it <PROGRAM>.COM and write it to
  826. disk.  If it  doesn't,  you trace through the old code,  enter  new code,  and  try again.
  827. Eventually,  you come up with a program that works through trial-and-error.  However, this
  828. can lead to sloppy programming habits and inefficient code,  so it is important to bear in
  829. mind what you want a particular program to accomplish.
  830.  
  831. DEBUG  has some  limitations.   Most importantly,  it only recognizes  absolute addresses.
  832. When you write a program for  submission to an  assembler,  you label the instructions and
  833. data you will need to refer to, then refer to them with the label.  You don't need to know
  834. the actual addresses.   DEBUG, on the other hand, obliges you to look through your program
  835. listing and find addresses whenever you refer to them.   For instance, instead of entering
  836. JMP EXIT,  you must enter JMP 02FC.  Instead of CALL HEXPRINT, you use CALL 05AE.  Instead
  837. of MOV BX,  OFFSET DATA,  you need MOV BX,  0105.  If your routine is small, this does not
  838. present a problem.  But as you add features and it becomes larger,  this becomes a serious
  839. impediment.   If you add or alter instructions,  thereby changing an absolute address, you
  840. have to change every reference to it.   And the only way to find the references is to page
  841. through the entire program,  line by line.   For this  reason,  DEBUG is best for creating
  842. short utility programs.
  843.  
  844. Most often,  programs  created with  DEBUG use BIOS  or  DOS interrupts  to manipulate the
  845. hardware.   Some typical functions that appear in this  issue are setting the  cursor (see
  846. the  example on  page 4-72C  of the Developer's  Release Reference Manual  and the program
  847. listed in this issue), manipulating the shift keys, or swapping printer ports. Programs of
  848. this type should not contain any subroutines.
  849.  
  850. DEBUG has another important limitation: it only understands hexadecimal numbers.  There is
  851. simply nothing you can do to make it accept decimal numbers.   This is not a  problem when
  852. entering addresses or interrupt numbers, as most assembly language programmers think these
  853. values  in  hexadecimal  anyway.   But  very  few  programmers  think  in  hex  when doing
  854. calculations.   DEBUG is  therefore not a good  tool  for doing  number-crunching  of even
  855. intermediate complexity.   Although  there  are  utilities  available  to  assist  in this
  856. process,  such as Sidekick, this is still a major obstacle to doing extensive calculations
  857. within DEBUG.
  858.  
  859. Another problem with DEBUG is that code produced with it can be extremely obscure.  Trying
  860. to decipher the flow of a program where you  have only absolute  addresses and hexadecimal
  861. numbers to  guide you can be  very  frustrating.   In  addition,  DEBUG  does  not support
  862. comments.   So when  you read  a DEBUG listing,  you are,  for all  intents  and purposes,
  863. reading "machine English."  The machine expresses its own language in cryptic English-like
  864. symbols,  making a few grudging concessions to your desire to understand it.   All of this
  865. reinforces what we suggested earlier: keep DEBUG routines short.
  866.  
  867. The program from  the Developer's Release  Reference  Manual  mentioned  above  is  a good
  868. example of a program appropriate for DEBUG.  The listing on page 4-72C is as follows: 
  869.  
  870.                _PROG SEGMENT BYTE
  871.                ASSUME    CS:_PROG
  872.      ;
  873.      CURSOR    PROC       FAR       ; Force a far return.
  874.      ;
  875.                MOV        CX,[BX]    ; Get two HEX digits.
  876.                MOV        AH,1       ; Set cursor type.
  877.                INT        10H        ; Video interrupt.
  878.                RET                   ; Do a far return.
  879.      ;
  880.      CURSOR    ENDP
  881.      ;
  882.      _PROG     ENDS
  883.                END
  884.  
  885. This is  a terse routine that  converts  the dBASE III cursor  to  a  full-sized  box when
  886. CHR(18)  passed as a parameter to it.   Notice one thing about this code: it has six lines
  887. of assembler directives  (the  first three and the last  three),  and  only  four lines of
  888. machine instructions.   In  a short program  like  this  one,  there  is  no  advantage to
  889. assembling, linking, and converting it using MASM, LINK, and EXE2BIN.  DEBUG is faster and
  890. easier.
  891.  
  892. Here is a DEBUG session that enters this program as a .COM file.
  893.  
  894. (The DEBUG commands  are explained in  Chapter 8  of the PC/MS-DOS  manual.   Page numbers
  895. which follow refer to it.)
  896.  
  897.      D>debug
  898.  
  899. First give DEBUG the 'A' (assemble) command (page 8-15) and enter the program.
  900.  
  901.      -A
  902.      6257:0100  MOV CX,[BX]
  903.      6257:0102  MOV AH,1
  904.      6257:0104  INT 10
  905.      6257:0106  INT 20
  906.      6257:0108
  907.  
  908.  
  909. Notice that 'INT 20'  is our last instruction, not 'RET' as the manual indicates.  We will
  910. explain this shortly.
  911.  
  912. The address following the last instruction is 108.   Therefore,  enter eight into CX using
  913. the 'R'  (register)  command [page 8-41]. This tells DEBUG the number of bytes to write to
  914. disk.
  915.  
  916.      -RCX
  917.      CX 0000
  918.      :8
  919.  
  920. Name the program CURSOR.COM using the 'N'  command [page 8-37], and write it to disk using
  921. 'W' [page 8-55].
  922.  
  923.      -NCURSOR.COM
  924.      -W
  925.      Writing 0008 bytes
  926.  
  927. This is the  basic procedure for creating  a .COM  file from DEBUG.  CURSOR.COM will yield
  928. unpredictable results executed from PC/MS-DOS,  since the registers are not preserved, and
  929. we have no way of knowing what is being passed in DS:BX.   (When we tested it,  the cursor
  930. simply vanished.)  Nor, in its present form, will it work in dBASE III.  It needs a couple
  931. of changes to make it work, but this point deserves some attention.
  932.  
  933. PC/MS-DOS .COM files and dBASE LOAD modules require slightly  different specifications.  A
  934. .COM file must be ORGed (originated)  at address 100H, and it must end with a command like
  935. INT 20H (terminate) or INT 27H (terminate and stay resident); a simple RET will not return
  936. correctly.   dBASE III,  on the other hand, requires LOAD modules to be ORGed at address 0
  937. and to return to dBASE III with a far return, RETF.  If you load a conventional .COM file,
  938. ORGed at 100H and terminated with INT 20H, into dBASE III, and then call it, you will lock
  939. the system,  even if  it works from PC/MS-DOS.   When DEBUG writes  a program to  disk, it
  940. writes a  binary  file  --  that  is,  a  file  which  contains  nothing  but  the machine
  941. instructions  you have  given it.   Therefore,  we need not concern ourselves  with ORGing
  942. programs correctly at this stage. We do have to terminate LOAD modules with RETF, however.
  943. Here is a DEBUG session that enters this  program as a  .BIN file which will  execute from
  944. dBASE III.
  945.  
  946.      D>debug
  947.  
  948. Type 'A' for assemble.  Terminate with a RETF.
  949.  
  950.      -A
  951.      6346:0100  MOV CX,[BX]
  952.      6346:0102  MOV AH,1
  953.      6346:0104  INT 10
  954.      6346:0106  RETF
  955.      6346:0107
  956.  
  957. Place the number 7 in the CX register to save 7 bytes to disk.
  958.  
  959.      -RCX
  960.      CX 0000
  961.      :7
  962.  
  963. Name the file, and write it.
  964.  
  965.      -NCURSOR.BIN
  966.      -W
  967.      Writing 0007 bytes
  968.  
  969. Quit DEBUG.
  970.      -Q
  971.  
  972. The page of the Developer's Release Manual referred to  above gives the  following example
  973. of how to use Cursor:
  974.  
  975.      LOAD Cursor
  976.      STORE CHR(18) TO shape
  977.      CALL Cursor WITH shape
  978.  
  979. The commands to convert the cursor back to its normal format are:
  980.  
  981.      LOAD Cursor
  982.      STORE CHR(12) + CHR(11) to shape
  983.      CALL Cursor WITH shape
  984.  
  985.  
  986. .COM Files vs. .EXE Files
  987.  
  988. When creating programs with a full-featured assembler, we have two options: .COM files and
  989. .EXE files.  Each has advantages and disadvantages.
  990.  
  991. .COM files are an inheritance from the world of 8-bit CP/M.   They are your only option if
  992. you have a CP/M machine.  .COM files must adhere to a strictly defined structure.
  993.  
  994.      1.  They must fit entirely within one segment.   All segment registers  must point to
  995.         the same address, and cannot be changed during the execution of the program.  This
  996.         means that all of our main program,  subroutines, and data must fit in 64K.  A 64K
  997.         .COM file is a very large program --  each line of code assembles to between 1 and
  998.         6 bytes, so a 64K .COM file could have as many as 30,000 lines of source code.
  999.  
  1000.      2. They must be ORGed at 100H.  When PC/MS-DOS loads a .COM file, it jumps to CS:100H
  1001.         and begins executing.
  1002.  
  1003.      3.  They must return control to their calling routine with either INT 20H or INT 27H,
  1004.         or the equivalent INT 21H function calls, 4CH and 31H.
  1005.  
  1006. .COM files load more quickly than .EXE files,  since no addresses need to be calculated at
  1007. load time.
  1008.  
  1009. The  assembly  language programs that  dBASE II and  dBASE III can execute  as subroutines
  1010. (with the CALL command) are variations of the .COM file.  We will discuss the specifics of
  1011. their formats later.
  1012.  
  1013. .EXE  files  are  less  limited  structurally.    The  segment  registers  can  be  freely
  1014. manipulated,  and each one can point to an entirely different 64K segment.  .EXE files can
  1015. therefore  be  much  larger  than  .COM  files.   .EXE files were designed to  take better
  1016. advantage of the actual architecture of 16-bit 8086-based microprocessors.  Having data in
  1017. one segment,  code in another, and the stack in a third allows much greater utilization of
  1018. the memory  space available in  today's machines.   It  also provides us  the semblance of
  1019.  
  1020.  
  1021. structured programming in assembly language.  The SEGMENT,  PROC, ENDS, and ENDP operators
  1022. give  a program listing a much  more  organized appearance  than  it  has with  JMP and DB
  1023. statements interspersed throughout the code.
  1024.  
  1025. .EXE files take longer to load than .COM files,  as many of the absolute addresses are not
  1026. computed until load time.   They also take up more  disk space than .COM  files.  However,
  1027. since they use much more of the 8086 family's capabilities, they can be much more powerful
  1028. programs.  The commercial programs which were handed down from the CP/M world are all .COM
  1029. files,  whereas those which were created since the advent  of  16-bit  machines are mostly
  1030. .EXE files.
  1031.  
  1032. Having said this,  we will leave .EXE files behind.  You cannot LOAD .EXE files from dBASE
  1033. II or dBASE III.   You can execute them with QUIT TO in dBASE II  or RUN(!)  in dBASE III.
  1034. If you want  to  pass  parameters  to  and from  .EXE  files,  you must pass them  in text
  1035. files(the SDF format is recommended).
  1036.  
  1037.  
  1038. Adapting Assembly Language Programs to dBASE II or III
  1039.  
  1040. As mentioned earlier,  the format of a dBASE II  or III assembly language  subroutine most
  1041. closely resembles that of a .COM  file.  Most importantly,  it must reside in one segment.
  1042. Since  it  is  intended  as  a subroutine,  not as a stand-alone program,  it  will differ
  1043. somewhat from a standard .COM file.
  1044.  
  1045. For one thing,  a .COM file must be ORGed  at 100H.   However,  ORGing a dBASE (II or III)
  1046. subroutine at 100H will cause it to fail.   A program intended for use in dBASE II must be
  1047. ORGed high in the code segment --  the exact address depends on  the version of  dBASE II,
  1048. the later the version,  the higher the address.   In version 2.43*, the ORG address should
  1049. be above 61440 decimal. (See Robert Boies' article on swapping printer ports in the August
  1050. issue of TechNotes for a good example of a dBASE II assembly language program.)  A program
  1051. intended for dBASE III must be ORGed at 0  (that is,  it need not have an  ORG statement).
  1052. Secondly,  .COM files return to their caller with interrupts (usually 20H or 27H), whereas
  1053. dBASE II and dBASE III routines require RET (return)  --  near for dBASE II, far for dBASE
  1054. III.
  1055.  
  1056. The procedure for converting assembly language source code  into programs dBASE II  or III
  1057. can execute are as follows:
  1058.  
  1059.      1.  For dBASE II,  you must assemble your program with  an assembler  that produces a
  1060.         file  in  Intel .HEX  format.  Intel's assemblers,  ASM (for CP/M)  and ASM86 (for
  1061.         CP/M-86),  create  such  a file.   For PC/MS-DOS,  the  Seattle  Computer Products
  1062.         assembler generates a .HEX  file.   Refer  to  their  manuals,  as  their assembly
  1063.         language syntax differs somewhat from Microsoft's and IBM's.
  1064.  
  1065.      2.  For dBASE III,  use the IBM or Microsoft Macro-Assembler (MASM.EXE)  to produce a
  1066.         .OBJ (object) file.  Enter the command as follows:
  1067.  
  1068.              MASM <filename> <filename> <filename>;
  1069.  
  1070.         The third parameter will  cause  MASM  to  produce  a  listing  file  with  a .LST
  1071.         extension, which is very useful for debugging.
  1072.  
  1073.      3.  Use  the linker utility  (LINK.EXE)  that comes both with PC/MS-DOS  and with the
  1074.         assembler.  This will create an .EXE file.  The command is:
  1075.  
  1076.              LINK <filename>
  1077.  
  1078.         Press Return three times in response to the prompts.
  1079.  
  1080.      4.  Use EXE2BIN.EXE to  convert the program to  .COM  or  .BIN  format.   If  you are
  1081.         creating a .BIN file, you need only enter one parameter in the command line:
  1082.  
  1083.              EXE2BIN <filename>
  1084.  
  1085.         If you are creating a .COM file, you need to specify the full target filename:
  1086.  
  1087.              EXE2BIN <filename> <filename>.COM
  1088.  
  1089.  
  1090. Using Conditional Assembler Directives
  1091.  
  1092. Because the differences  between .COM files  and .BIN files  are minor,  it is possible to
  1093. generate both using the same source code.  The following program skeleton shows how to set
  1094. this up.  The EQU statements at the top inform the assembler whether we  are  assembling a
  1095. program for PC/MS-DOS or dBASE III.   In the present example,  we have set COM  equal to 0
  1096. (meaning false)  and D3  equal to  1  (non-zero,  meaning true).   We then use conditional
  1097. directives to tell the assembler how we want  the program created.  Conditional directives
  1098. are statements in  your assembly program to  direct the  assembler to assemble a  block of
  1099. instructions based on a variable value.  For example, IF COM (if COM is not zero), ORG the
  1100. program  at  offset  100H.   Then at the end of  the program,  IF COM,  exit with INT 20H;
  1101. otherwise, exit with a far RET.
  1102.  
  1103.         .LFCOND            ; List false conditionals,
  1104.          PAGE    60,132    ; page length 60, line 132.
  1105.  
  1106.     COM  EQU    0          ; Assemble program as .BIN
  1107.     D3   EQU    1          ; file for dBASE III.
  1108.  
  1109.     CODESEG    SEGMENT    BYTE PUBLIC 'CODE'
  1110.     ROUTINE PROC    FAR
  1111.          ASSUME CS:CODESEG,DS:CODESEG
  1112.  
  1113.          IF    COM
  1114.             ORG    100H
  1115.          ENDIF
  1116.  
  1117.          PUSH        DS        ; Make sure DS points to
  1118.          PUSH        CS        ; the current
  1119.          POP         DS        ; segment.
  1120.         .
  1121.         .    (program goes here)
  1122.         .
  1123.         .
  1124.  
  1125.          POP         DS        ; Restore caller's DS.
  1126.          IF    COM
  1127.            INT       20H       ; INT 20H if .COM file.
  1128.          ELSE
  1129.           RET                  ; Far return if dBASE III
  1130.          ENDIF
  1131.  
  1132.     ROUTINE  ENDP
  1133.     CODESEG  ENDS
  1134.               END
  1135.  
  1136. It is very important  to load the DS  register with the segment address  contained  in CS.
  1137. PC/MS-DOS does this automatically for a .COM file,  but dBASE III does not.  Therefore, if
  1138. your routine needs to access its own data, it will need to set DS correctly.
  1139.  
  1140.  
  1141. Sample Program With Conditional Assembly
  1142.  
  1143. Here is an program built on the skeletal structure which sets condensed print on  an EPSON
  1144. printer.
  1145.  
  1146.  ; Program ...: Printer.ASM
  1147.  ; Author ....: Ralph Davis
  1148.  ; Date ......: September 1, 1985
  1149.  
  1150.  TITLEPRINTER.ASM -- sets condensed print
  1151.  
  1152.   .LFCOND
  1153.  PAGE60,132
  1154.  
  1155.  COMEQU0
  1156.  D3EQU1
  1157.  
  1158.  CODESEGSEGMENTBYTE PUBLIC 'CODE'
  1159.  PRINTER PROCFAR
  1160.  ASSUME CS:CODESEG,DS:CODESEG
  1161.  
  1162.   IFCOM
  1163.      ORG100H
  1164.   ENDIF
  1165.  
  1166.  START:JMPSHORT ENTRY; Jump past data.
  1167.  CODESDB27,64,27,15; Printer control codes.
  1168.  CODELEN EQU$-CODES; Length of string.
  1169.  ENTRY: PUSHAX; Save registers.
  1170.  PUSHBX
  1171.  PUSHDS
  1172.  PUSHCS; Set up DS
  1173.  POPDS; with current segment.
  1174.  PUSHCX; Save CX
  1175.  PUSHDX; and DX.
  1176.   MOV     BX,OFFSET CODES; Point BX to codes.
  1177.  MOVCX,CODELEN; Length of string.
  1178.  ; Controls the loop.
  1179.  GET_CODE:
  1180.  MOV     DL,BYTE PTR [BX]       ; Get code to send.
  1181.  MOV     AH,5H                  ; PC/MS-DOS function 5H,
  1182.  INT     21H                    ; (send char to printer).
  1183.   INC     BX                     ; Point to next code
  1184.   LOOP    GET_CODE               ; and print it.
  1185.  
  1186.  POPDX; Restore registers.
  1187.  POPCX
  1188.  POPDS
  1189.  POPBX
  1190.  POPAX
  1191.  
  1192.   IFCOM
  1193.           INT     20H           ; INT 20H if .COM file.
  1194.   ELSE
  1195.    RET; Far return to dBASE III.
  1196.   ENDIF
  1197.  
  1198.  PRINTER  ENDP
  1199.  CODESEG  ENDS
  1200.   ENDSTART; End assembly.
  1201.  
  1202.  
  1203. Assemble this program according to the instructions given  earlier.   To run it from dBASE
  1204. II or dBASE III versions 1.0  and 1.1, assemble it as a .COM file, and enter the following
  1205. commands:
  1206.  
  1207.  dBASE II:
  1208.  
  1209.       QUIT TO 'Printer'
  1210.  
  1211.  dBASE III:
  1212.  
  1213.       RUN Printer
  1214.  
  1215. To run it from the Developer's Release of dBASE III,  assemble it as a .BIN  file, and use
  1216. these commands:
  1217.  
  1218.       LOAD Printer
  1219.       CALL Printer
  1220.  
  1221.  
  1222. 7  dBASE III Version 1.1 Change Summary
  1223.